package com.dzy.interceptor;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.xml.soap.SOAPException;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.invoker.MethodDispatcher;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Description
 * webservice拦截器入口，在调用方法前做ip白名单验证并记录请求日志
 * @author Dzy(yndxc@163.com)
 * created by 2016年11月24日下午5:02:59
 */
public class AuthInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
	private final static Logger logger = LoggerFactory.getLogger(AuthInInterceptor.class);
	
	private List<String> ipList;
	public AuthInInterceptor(List<String> ipList) {
		//因为AbstractPhaseInterceptor没有无参的构造函数，所以子类定义一个构造函数显式调用父类的带参构造函数，  
        //避免子类隐式调用父类无参构造函数导致错误  
        super(Phase.PRE_INVOKE);//表示拦截器作用的阶段，在Phase中定义有很多常量表示 ，这里我们作用到调用方法前
        this.ipList = ipList;
	}
	

	
	@Override
	public void handleMessage(SoapMessage message) throws Fault {
		HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
		boolean flag = false;
		String ipAddress = getUsrIPAddr(request);// 获取客户端的IP地址
		for (String ipString : ipList) {
			if (ipAddress.equals(ipString)) {
				flag = true;
				break;
			}
		}
		// 获取方法信息  
		Exchange exchange = message.getExchange();  
		Collection<BindingOperationInfo> bop = exchange.getBinding().getBindingInfo().getOperations();
		MethodDispatcher md = (MethodDispatcher) exchange.get(Service.class).get(MethodDispatcher.class.getName());  
		Method method = md.getMethod((BindingOperationInfo)(bop.toArray()[0]));
		//如何获取参数
		List<?> content = message.getContent(List.class);  
		if (flag) {
			logger.info("IP [" + ipAddress + "] invoke ["+method+"]");
			logger.info("params "+content);
		}else{
			String errorMsg = "IP [" + ipAddress + "] 不被允许访问";
			logger.error(errorMsg+"["+method+"]");
			throw new Fault(new SOAPException(errorMsg)); 
		}
	}
 
	/** 
     * 获取客户端ip地址   
     * @param request 
     * @return 
     */  
    public String getUsrIPAddr(HttpServletRequest request) {  
        String ip = "";  
        //1.首先考虑有反向代理的情况，如果有代理，通过“x-forwarded-for”获取真实ip地址  
        ip = request.getHeader("x-forwarded-for");  
        //2.如果squid.conf的配制文件forwarded_for项默认是off，则：X-Forwarded-For：unknown。考虑用Proxy-Client-IP或WL-Proxy-Client-IP获取  
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("Proxy-Client-IP");  
        }  
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("WL-Proxy-Client-IP");  
        }  
        //3.最后考虑没有代理的情况，直接用request.getRemoteAddr()获取ip  
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getRemoteAddr();  
        }  
        //4.如果通过多级反向代理，则可能产生多个ip，其中第一个非unknown的IP为客户端真实IP（IP按照','分割）  
        if(ip != null && ip.split(",").length > 1){  
            ip = (ip.split(","))[0];  
        }          
        //5.如果是服务器本地访问，需要根据网卡获取本机真实ip  
        if("127.0.0.1".equals(ip)) {  
            try {  
                ip = InetAddress.getLocalHost().getHostAddress();  
            } catch (UnknownHostException e) {  
                logger.error(e.getMessage(),e);//获取服务器(本地)ip信息失败  
                return "";  
            }  
        }  
//        6.校验ip的合法性，不合法返回""  
        if(!isValidIp(ip)) {  
            return "The ip is invalid.";  
        }else {  
            return ip;  
        }  
    }  
  
    /** 
     * 判断是否为合法IP地址 
     * @param ipAddress 
     * @return 
     */  
    private boolean isValidIp(String ipAddress) {  
        boolean retVal = false;  
        try {  
            if(ipAddress!=null && !"".equals(ipAddress)){  
                Pattern pattern = Pattern.compile("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}");  
                retVal = pattern.matcher(ipAddress).matches();  
            }              
        } catch(Exception e){  
            logger.error(e.getMessage(), e);  
        }  
        return retVal;  
    }  
}
